package com.example.kick.java8;

import com.example.kick.bean.Player;
//import io.lettuce.core.Limit;
//import io.netty.util.internal.MathUtil;

import java.math.RoundingMode;
import java.util.*;
import java.util.concurrent.ForkJoinTask;
import java.util.concurrent.atomic.DoubleAccumulator;
import java.util.function.DoubleUnaryOperator;
import java.util.function.UnaryOperator;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;

import static java.util.stream.Collectors.*;

public class StreamApiExam {

    public static List<Player> initPlayerList() {
        Player p1 = new Player("Michael", 60, "Chicago Bulls", true);
        Player p2 = new Player("Pippen", 60, "Chicago Bulls", true);
        Player p3 = new Player("Dennis", 59, "Chicago Bulls", true);
        Player p4 = new Player("Steve Kerr", 60, "Chicago Bulls", true);
        Player p5 = new Player("John Pacson", 55, "Chicago Bulls", false);
        Player p6 = new Player("Bill Cartlat", 56, "Chicago Bulls", false);
        Player p7 = new Player("Stephen Curry", 35, "Warriors", true);
        Player p8 = new Player("Green", 33, "Warriors", false);
        Player p9 = new Player("Tompson", 35, "Warriors", true);
        Player p10 = new Player("Kevin", 36, "Nets", true);
        Player p11 = new Player("Owen", 32, "Nets", false);
        Player p12 = new Player("James Hardon", 33, "Nets", true);
        List<Player> playerList = Arrays.asList(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12);
        return playerList;
    }

//    public static void main(String[] args) {
//        testFeiboNaqiNumbers(10);
//        System.out.println(-1<<5);
//        System.out.println(1<<5);
//        System.out.println(2<<5);
//        System.out.println(2<<4);
//        System.out.println(2>>4);
//        System.out.println(2>>1);
//        System.out.println(2>>0);
//        for (int i = 0; i <= 200; i++) {
//            double number = Math.random() * 8 + 2;
//            System.out.print((int) number + ",");
//        }
//        System.out.println();
//        Random random = new Random();
//        for (int j = 0; j <= 200; j++) {
//            int number = random.nextInt(10);
//            System.out.print(number + ",");
//        }
//        try {
//            for (int i = 0; i <= 100; i++) {
//                Thread.sleep(1000);
//                new Thread(() -> {
//                    System.out.println(Thread.currentThread().getName() + ",time:" + System.currentTimeMillis());
//                }).start();
//            }
//        } catch (InterruptedException e) {
//            e.printStackTrace();
//        }
//
//    }

    public static void main(String[] args) {
        createlimitGouGuNumbers(20);
    }

    /**
     * 体验flatMap的作用：它的作用是扁平化处理流,将流中所有元素映射成一个流,即流的内容,而不是将流中的所有元素都映射成一个单独的流
     * 题目内容:返回一张列表,列出里面各不相同的字符. 例如,给定单词列表["java","scala"],你想要返回列表["j","a","v","s","c","l"]
     *
     * @param list
     */
    public static void testFlatMap(List<String> list) {
        //错误示例：
        // 1.第一步list.stream().map(word -> word.split(""))产生了一个字符串数组流,即Stream<String[]>
        // 2.第二步map(Arrays:stream)把第一步中的每一个数组都映射成了一个对应的单独流(非预期结果)，所以第二步后面的操作都没用了
        List<Stream<String>> resultList1 = list.stream().map(word -> word.split(""))
                .map(Arrays::stream)
                .distinct()
                .collect(toList());
        //打印结果: java.util.stream.ReferencePipeline$Head@f2a0b8e,java.util.stream.ReferencePipeline$Head@593634ad (非预期结果)
        resultList1.stream().forEach(s -> System.out.print(s + ","));
        System.out.println();

        //正确示例:第二步用了"flatMap"而非"map"映射,flatMap的效果并不是把第一步中的各个数组分别映射成一个流,而是将所有数组映射成一个流,即映射成流的内容
        List<String> resultList = list.stream().map(word -> word.split(""))
                .flatMap(Arrays::stream)
                .distinct()
                .collect(toList());
        resultList.stream().forEach(s -> System.out.print(s + ","));
    }

    /**
     * 依然是flatMap的使用场景
     * 题目:给定两个数字列表，如何返回所有的数对呢？例如，给定列表[1, 2, 3]和列表[3, 4]，应
     * 该返回[(1, 3), (1, 4), (2, 3), (2, 4), (3, 3), (3, 4)]。为简单起见，你可以用有两个元素的数组来代表数对。
     *
     * @param l1
     * @param l2
     */
    public static void createNumberArrayListByListNumbers(List<Integer> l1, List<Integer> l2) {
        List<int[]> result = l1.stream().
                flatMap(n1 -> l2.stream().map(n2 -> new int[]{n1, n2}))
                .collect(toList());
        result.stream().forEach(numbers -> System.out.println(numbers[0] + "," + numbers[1]));
    }

    /**
     * 选出100内的所有勾股数
     * 重点: 1.box()装箱 2.flatMap扁平化处理流 3.mapToObj将原始值流生成对象流
     *
     * @param limit
     * @return
     */
    public static Stream<double[]> createlimitGouGuNumbers(int limit) {
        //方法1：生成一个int数据流,但此方法的缺点是计算平方数要计算两次
        Stream<int[]> gouGuStream = IntStream.rangeClosed(1, limit).boxed()
                .flatMap(a -> IntStream.rangeClosed(a, limit)
                        .filter(b -> Math.sqrt(a * a + b * b) % 1 == 0) //第一次计算平方数
                        .mapToObj(b -> new int[]{a, b, (int) Math.sqrt(a * a + b * b)})); //第二次计算平方数
        gouGuStream.forEach(array -> System.out.println(array[0] + "," + array[1] + "," + array[2]));

        System.out.println();
        //方法2:生成一个double数据流,先计算所有数据限制范围内的平方数,再从中筛选中勾股数,好处是平方数只计算一次
        Stream<double[]> pythagoreanStream = IntStream.rangeClosed(1, limit).boxed()
                .flatMap(a -> IntStream.rangeClosed(a, limit)
                        .mapToObj(b -> new double[]{a, b, Math.sqrt(a * a + b * b)})).filter(t -> t[2] % 1 == 0);
        pythagoreanStream.forEach(array -> System.out.println((int) array[0] + "," + (int) array[1] + "," + (int) array[2]));
        return pythagoreanStream;
    }

    public static void testGouGuNumbers(int limitNumber) {
        Stream<double[]> stream = IntStream.rangeClosed(1, limitNumber).boxed()
                .flatMap(a -> IntStream.rangeClosed(a, 100)
                        .mapToObj(b -> new double[]{a, b, Math.sqrt(a * a + b * b)}))
                .filter(t -> t[2] % 1 == 0);

        Queue<String> linkedList = new LinkedList<>();
        Deque<String> queueList = new LinkedList<>();
    }

    /**
     * 计算斐波那契数列
     * 要点：1.Stream.iterate  2.mapToInt
     *
     * @param limitNumber
     */
    public static void createFibonacciNumbers(int limitNumber) {
//        Stream.iterate(new int[]{0, 1}, t -> new int[]{t[1], t[1] + t[0]}).limit(limitNumber).forEach(t -> System.out.println(t[0] + "," + t[1]));
        IntStream result = Stream.iterate(new int[]{0, 1}, t -> new int[]{t[1], t[0] + t[1]}).limit(limitNumber).mapToInt(t -> t[0]);
        result.forEach(n -> System.out.print(n + ","));
    }

    public static void testCollectingAndThen() {
        System.out.println();
//        List<Player> playerList = initPlayerList();
//        int result = playerList.stream().collect(collectingAndThen(toList(), List::size));
//        System.out.println(result);
        long start = System.currentTimeMillis();
        int total = IntStream.rangeClosed(1, 10000).sum();

        System.out.println(total + ",Non parallel takes: " + (System.currentTimeMillis() - start) + " milli seconds");
    }

    public static void testFeiboNaqiNumbers(int limitNumber) {
        IntStream resultStream = Stream.iterate(new int[]{0, 1}, t -> new int[]{t[1], t[0] + t[1]}).limit(limitNumber).mapToInt(t -> t[0]);
        Stream<int[]> stream1 = Stream.iterate(new int[]{0, 1}, t -> new int[]{t[1], t[0] + t[1]}).limit(limitNumber);
        stream1.forEach(array -> System.out.println(array[0] + "," + array[1]));
        System.out.println();
        resultStream.forEach(n -> System.out.print(n + " "));
    }

    public static void testParallelCalculation() {
        System.out.println();
//        long start = System.currentTimeMillis();
//        int total = IntStream.rangeClosed(1, 10000).parallel().sum();
//        System.out.println(total + ",Parallel takes: " + (System.currentTimeMillis() - start) + " milli seconds");

        UnaryOperator<String> u1 = (String text) -> "hello " + text;
        UnaryOperator<String> u2 = (String text) -> text + ", ping pang";
        System.out.println(u1.andThen(u2).apply("world"));
        ;
    }

    public static void testGroupingBy() {
        List<Player> playerList = initPlayerList();
        Map<Integer, List<Player>> playAgeMap = playerList.stream().collect(Collectors.groupingBy(Player::getAge));
        playAgeMap.forEach((age, pList) -> {
            System.out.print("age: " + age + ",player list: {");
            pList.stream().forEach(p -> System.out.print(" " + p.getPlayerName() + ","));
            System.out.println("}");
        });

        Map<Boolean, Map<String, List<Player>>> map1 = playerList.stream().collect(groupingBy(Player::isHallMember, groupingBy(Player::getTeam)));
        Map<Boolean, List<Player>> map2 = playerList.stream().collect(groupingBy(Player::isHallMember));
        Map<String, List<Player>> map3 = playerList.stream().collect(groupingBy(Player::getTeam));
    }

    /**
     * 测试Collectors的maxBy、minBy、reduce等方法
     */
    public static void testMaxByOrMinBuy() {
        List<Player> playerList = initPlayerList();
//        System.out.println("player list size: " + playerList.size() + ",stream count: " + playerList.stream().count());
//        System.out.println(playerList.stream().collect(Collectors.counting()));
        int totalAge = playerList.stream().collect(Collectors.summingInt(Player::getAge));
        System.out.println("totalAge: " + totalAge);
//        IntSummaryStatistics ageStatistic = playerList.stream().collect(Collectors.summarizingInt(Player::getAge));
//        System.out.println(ageStatistic);
        playerList.stream().forEach(p -> System.out.println(p.getPlayerName() + "." + p.getAge()));
        playerList.sort(Comparator.comparing(Player::getAge));
        System.out.println();
        playerList.stream().forEach(p -> System.out.println(p.getPlayerName() + "." + p.getAge()));
        System.out.println();
        Comparator<Player> comparator = Comparator.comparing(Player::getAge);
        Optional<Player> result = playerList.stream().collect(Collectors.maxBy(comparator));
        System.out.println(result.get().getPlayerName() + "," + result.get().getAge());

        String allNames = playerList.stream().map(Player::getPlayerName).collect(Collectors.joining(", "));
        System.out.println(allNames);

        //Collectors.counting()也可以由reduce归约操作来表示,即把集合中的每个对象映射成数据1，就可以统计出集合中的对象个数
        long totalCount = playerList.stream().collect(Collectors.reducing(0L, p -> 1L, Long::sum));
        System.out.println("totalCount: " + totalCount);

        int ageResult = playerList.stream().collect(Collectors.reducing(0, Player::getAge, (a1, a2) -> a1 + a2));
        System.out.println("ageResult: " + ageResult);
    }

//    public static void main(String[] args) {
////        testGroupingBy();
////        testCollectingAndThen();
//        testParallelCalculation();
////        testMaxByOrMinBuy();
////        createlimitGouGuNumbers(100);
////        createFibonacciNumbers(20);
//
////        List<String> list = Arrays.asList("hello", "world");
////        testFlatMap(list);
//
////        createNumberArrayListByListNumbers(Arrays.asList(2,3),Arrays.asList(2,4,5,6));
////        Player player = new Player("Stephen", true);
////        Player player1 = new Player("Green", false);
////        Player player2 = new Player("Kevin", false);
////        Player player3 = new Player("Tompson", true);
////        Player player4 = new Player("Steve Kerr", true);
////        List<Player> playerList = Arrays.asList(player, player1, player3, player4);
////        List<Player> hallPlayerList = playerList.stream().filter(p -> !p.isHallMember()).collect(Collectors.toList());
////        hallPlayerList.stream().forEach(p -> System.out.println(p.getPlayerName() + "," + p.isHallMember()));
////        System.out.println();
////        System.out.println(playerList.stream().allMatch(p -> p.isHallMember()));
////        System.out.println(2 % 1);
////        System.out.println(2.3 % 1);
//    }

}
